#include <iostream>

class Complex
{
public:
    // 定义一个复数
    // 构造一个无参的
    Complex():real(0), vir(0){}
    // 构造一个带参的
    Complex(int real, int vir):real(real),vir(vir){}
    void show()
    {
        std::cout<<real<<"+"<<vir<<"i"<<std::endl;
    }

    Complex(const Complex &c)
    {
        std::cout<<"拷贝构造！\n";
        real = c.real;
        vir = c.vir;
    }

    // 两个复数相加
    // 表达上不够简洁，能不能把复数的相加也表示成c1+c2的形式？
    Complex Add(const Complex &c)
    {
        Complex result;
        result.real = this->real + c.real;
        result.vir = this->vir + c.vir;
        return result;
    }

    // 运算符重载（给已有的运算符赋予全新的功能）
    // 本质就是函数调用，并不是一种全新的语法，顶多算是简化语义
    
    // 重载 +
    // 让加法实现自定义的Complex类的相加
    // operator关键字（运算符）
    Complex operator+(const Complex &c)
    {
        Complex result;
        result.real = this->real + c.real;
        result.vir = this->vir + c.vir;
        return result;
    }

    // 重载 -
    Complex operator-(const Complex &c)
    {
        Complex result;
        result.real = this->real - c.real;
        result.vir = this->vir - c.vir;
        return result;
    }

    // 重载 ++
    // 前置++和后置++都叫operator++，想区分它们就得构成重载，而构成重载的前提是参数不一样，但是++没有操作数，也就是它们都没有参数，为了区别它们，引入了占位参数
    // 占位参数：既要占一个重载的位置，又没有实际的意义

    // 前置++（++a）：返回左值，可以传引用
    // 给出去的是左值的引用，自己变值就变，所以说给的是a
    // 助记：a就是++的操作数，所以不需要额外的操作数了
    Complex &operator++()
    {
        real++;
        vir++;
        return *this;   // 返回自己（this是当前对象的指针，*this就是当前对象）
        // *this是个左值，如果返回一个左值就会发生拷贝，为了减少拷贝构造，所以要传引用
    }

    // 后置++（a++）：返回右值，不可以传引用
    // 给的是右值，这个值出去了就变不了了
    // 助记：++后面没有操作数，所以需要加一个操作数，虽然这个参数实际不存在
    Complex operator++(int)
    {
        Complex result = *this; // 这里发生了拷贝
        real++;
        vir++;
        return result;  // 这里返回局部变量，不能传引用
    }

    // 重载 << （自定义类型的对象想直接打印输出需要重载输出运算符）
    // ostream这个类从C++11以后，这个对象就不允许拷贝了，所以要传引用
    // void operator<<(std::ostream &os)
    // {
    //     os<<real<<"+"<<vir<<"i"<<std::endl;
    // }
    
    friend std::ostream &operator<<(std::ostream &os, const Complex &c);
    friend std::istream &operator>>(std::istream &is, Complex &c);
private:
    int real;
    int vir;
};

// 重载 <<
// 输入输出运算符只能进行全局重载
// 把它变成全局重载而不是成员函数重载的原因是：因为如果是成员函数重载，调用的时候一定是c1.operator，这样去掉.operator之后一定是c1在前
// 而把它变成全局重载之后，real和vir的访问又会出问题，因为是私有的，所以还要进行友元函数声明
// 要想能连续输出，得有返回值
// std::ostream &operator<<(std::ostream &os, const Complex &c)
// {
//     os<<c.real<<"+"<<c.vir<<"i"<<std::endl;
//     return os;
// }

// 重载 >>
// 把数据输入到某个对象中去，所以不能用const
std::istream &operator>>(std::istream &is, Complex &c)
{
    is>>c.real>>c.vir;
    return is;
}

int main()
{
#if 1
    // 两个复数相加
    Complex c1(1, 2);
    Complex c2(2, 3);
    c1.show();
    c2.show();
    std::cout << c1 << std::endl;

    Complex c = c1.Add(c2);
    c.show();
#endif

#if 0
    // + 重载
    Complex c1(1, 2);
    Complex c2(2, 3);
    c1.show();
    c2.show();

    // Complex c = c1.operator+(c2);    // 编译器在处理这个函数调用的时候，可以简化它的写法操作
    Complex c = c1 + c2;    // 简化了代码，并且语义上更便于理解
    // c1+c2返回值是函数里定义的局部变量，返回的是右值，不会发生拷贝构造（RVO优化）
    c.show();
#endif

#if 0
    // - 重载
    Complex c1(1, 2);
    Complex c2(2, 3);
    c1.show();
    c2.show();

    Complex c = c1 - c2;
    c.show();
#endif

#if 0
    // 前置++ 重载
    Complex c1(1, 2);
    Complex c2(2, 3);
    c1.show();
    c2.show();

    Complex &c = ++c1;  // 传引用过来得用引用去接
    c.show();
#endif

#if 0
    // 后置++ 重载
    Complex c1(1, 2);
    Complex c2(2, 3);
    c1.show();
    c2.show();

    Complex c = c1++;  // 返回的是一个右值，不会拷贝（RVO优化）
    c.show();
    c1.show();
#endif

#if 0
    // << 重载
    Complex c1(1, 2);
    Complex c2(2, 3);
    // c1.operator<<(std::cout);    // 正常调用形式，然后再把.operator去掉
    // c1<<std::cout;   // 输入输出运算符不允许进行成员函数重载，因为调用的时候写法是反过来的，很不科学
    
    // operator<<(std::cout, c1);   // 变成全局函数之后，正常调用的写法是这样的
    std::cout<<c1;  // 再简化完，这就符合一贯的风格了

    // std::cout<<c1<<c2;   // 之前连续输出的形式
    // operator<<(operator<<(std::cout, c1), c2)    // 本质操作
    // 所以要想连续输出，就得有返回值
    std::cout<<c1<<c2;  // 实现连续输出
#endif

#if 0
    Complex c;
    std::cin>>c;
    std::cout<<c;
#endif

    return 0;
}
